#pragma once
// 封装套接字
#include <iostream>
#include <string>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <memory>
#include "InetAddr.hpp"
using namespace std;
// 模板方式
namespace socket_ns
{
    class Socket;
    const int gbacklog = 8;
    using socket_sptr = shared_ptr<Socket>;
    // 套接字类
    class Socket
    {
    public:
        // 创建套接字
        virtual void CreatSocketOrDie() = 0;
        // 绑定套接字
        virtual void BindSocketOrDie(InetAddr &addr) = 0;
        // 监听套接字
        virtual void ListenSocketOrDie() = 0;
        // 服务端获取连接
        virtual socket_sptr Accepter(InetAddr *addr) = 0;
        // 客户端连接服务器
        virtual bool Connetcor(InetAddr &addr) = 0;
        // 获取套接字
        virtual int SockFd() = 0;
        // 读取信息
        virtual int Recv(string *out) = 0;
        // 发送信息
        virtual int Send(const string &in) = 0;

        // 创建监听套接字
        void BulidListenSocket(InetAddr &addr)
        {
            // 创建
            CreatSocketOrDie();
            // 绑定
            BindSocketOrDie(addr);
            // 监听
            ListenSocketOrDie();
        }
        // 创建客户端套接字
        bool BulidClientSocket(InetAddr &addr)
        {
            // 创建
            CreatSocketOrDie();
            // 客户端连接服务器
            return Connetcor(addr);
        }
    };

    // Tcp套接字类
    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int fd = -1)
            : _sockfd(fd)
        {
        }
        // 获取套接字
        virtual int SockFd() override
        {
            return _sockfd;
        }
        // 创建套接字
        void CreatSocketOrDie() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                cout << "创建套接字失败" << endl;
                exit(-1);
            }
            cout << "套接字创建成功 套接字文件描述符" << _sockfd << endl;
        }
        // 绑定套接字
        void BindSocketOrDie(InetAddr &addr) override
        {
            struct sockaddr_in peer;
            peer.sin_family = AF_INET;
            peer.sin_port = htons(addr.Port());
            peer.sin_addr.s_addr = inet_addr(addr.Ip().c_str());

            int n = bind(_sockfd, (struct sockaddr *)&peer, sizeof(peer));
            if (n < 0)
            {
                cout << "绑定失败" << endl;
                exit(-1);
            }
            cout << "绑定成功" << endl;
        }
        // 监听套接字
        void ListenSocketOrDie() override
        {
            int n = listen(_sockfd, gbacklog);
            if (n < 0)
            {
                cout << "将套接字设置为监听状态失败" << endl;
                exit(-1);
            }
            cout << "将套接字设置为监听状态成功" << endl;
        }
        // 服务端获取连接
        socket_sptr Accepter(InetAddr *addr) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int fd = accept(_sockfd, (struct sockaddr *)&peer, &len);
            if (fd < 0)
            {
                cout << "服务端获取连接失败" << endl;
                return nullptr;
            }
            cout << "服务端获取连接成功" << endl;
            *addr = peer;                                  // 将对方的网络信息传递给*addr
            socket_sptr sock = make_shared<TcpSocket>(fd); // 将通信套接字传递出去
            return sock;
        }
        // 客户端连接服务器
        bool Connetcor(InetAddr &addr) override
        {
            struct sockaddr_in peer;
            peer.sin_family = AF_INET;
            peer.sin_port = htons(addr.Port());
            peer.sin_addr.s_addr = inet_addr(addr.Ip().c_str());

            int n = connect(_sockfd, (struct sockaddr *)&peer, sizeof(peer));
            if (n < 0)
            {
                cout << "客户端获取连接失败" << endl;
                return false;
            }
            return true;
        }
        // 读取信息
        int Recv(string *out) override
        {
            char inbuffer[1024];
            int n = recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);
            if (n > 0)
            {
                // 读取成功
                inbuffer[n] = '\0';
                *out += inbuffer;
                return n;
            }
            else if (n == 0)
            {
                // 客户端退出
                return n;
            }
            else
            {
                // 读取失败
                return n;
            }
        }
        // 发送信息
        int Send(const string &in) override
        {
            int n = send(_sockfd, in.c_str(), in.size(), 0);
            return n;
        }

    private:
        int _sockfd; // 套接字
    };
}
